<?php
// +----------------------------------------------------------------------
// | User: zq
// +----------------------------------------------------------------------
// | Time: 2021/12/7 14:55
// +----------------------------------------------------------------------

namespace App\Models;

use Carbon\Carbon;
use DateTimeInterface;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;

/**
 * App\Models\BaseModel
 *
 * @method static \Illuminate\Database\Eloquent\Builder|BaseModel newModelQuery()
 * @method static \Illuminate\Database\Eloquent\Builder|BaseModel newQuery()
 * @method static \Illuminate\Database\Eloquent\Builder|BaseModel query()
 * @mixin \Eloquent
 */
class BaseModel extends Model
{
    use BooleanSoftDeletes;

    const CREATED_AT = 'add_time';
    const UPDATED_AT = 'update_time';

    public function getTable()
    {
        return $this->table ?? Str::snake(class_basename($this));
    }

    protected static function new()
    {
        return new static();
    }

    public function toArray()
    {
        $items = parent::toArray();
        $items = array_filter($items, function ($item) {
            return !is_null($item);
        });
        $keys = array_keys($items);
        $keys = array_map(function ($key) {
            return lcfirst(Str::studly($key));
        }, $keys);
        $values = array_values($items);
        return array_combine($keys, $values);
    }

    // 重写格式化时间
    public function serializeDate(DateTimeInterface $date)
    {
        return Carbon::instance($date)->toDateTimeString();
    }

    /**
     * 乐观锁更新(先比较后更新) compare and save
     *
     * 三个问题
     * 1. 如果这条数据在数据库中不存在,则直接抛出异常
     * 2. 获取一个模型对象,这个对象没有更新过,此时更新吗?
     * 3. 被更新的字段如果不存在?
     * @return bool|int
     */
    public function cas()
    {
        // 1. 如果这条数据在数据库中不存在,则直接抛出异常
        throw_if(!$this->exists, \Exception::class, 'model not exists when cas!');

        $dirty = $this->getDirty();  //得到这个对象上,哪些值发现了改变

        // 2. 获取一个模型对象,这个对象没有更新过,此时更新吗?
        if (empty($dirty)) {
            return 0;
        }

        if ($this->usesTimestamps()) {
            $this->updateTimestamps();
            $dirty = $this->getDirty();
        }

        $diff = array_diff(array_keys($dirty), array_keys($this->original));
        throw_if(!empty($diff), \Exception::class, 'key [' . implode(',', $diff) . '] not exists when cas');

        if ($this->fireModelEvent('casing') === false) {
            return 0;
        }

        // 3. 被更新的字段如果不存在?
        $query = $this->newModelQuery()->where($this->getKeyName(), $this->getKey()); //$this->newModelQuery() sql中不会加上 deleted=0 这个条件
        foreach ($dirty as $key => $value) {
            $query = $query->where($key, $this->getOriginal($key));  // $this->getOriginal($key) 获取原来没有修改前的数据
        }

        $row = $query->update($dirty);
        if ($row > 0) {
            $this->syncChanges();
            $this->fireModelEvent('cased', false);
            $this->syncOriginal();
        }

        return $row;
    }

    /**
     * Register a casing model event with the dispatcher.
     *
     * @param  \Closure|string  $callback
     * @return void
     */
    public static function casing($callback)
    {
        static::registerModelEvent('casing', $callback);
    }

    /**
     * Register a cased model event with the dispatcher.
     *
     * @param  \Closure|string  $callback
     * @return void
     */
    public static function cased($callback)
    {
        static::registerModelEvent('cased', $callback);
    }


}